package com.vxb.testmvp.test_rxjava;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import com.vxb.testmvp.R;
import com.vxb.testmvp.test_rxjava.model.Course;
import com.vxb.testmvp.test_rxjava.model.Student;

import java.util.ArrayList;
import java.util.List;

import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

public class TestRxJaveActivity extends AppCompatActivity {

    final String tag = "tag";

    private ImageView imageView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rx);

        imageView = (ImageView) findViewById(R.id.imageview);

        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(TestRxJaveActivity.this, TestRxJaveBigActivity.class);
                startActivity(intent);
            }
        });


        //testRxjava();
        //buwanzhenghuidiao();
        //  ceshicuowuqingkuangxiafangfadiaoyong();
        //  changjingjuli1();
        changjingjuli2();
        // xianchengcaozuoceshi1();
        // xianchengcaozuoceshi2();

    }

    // 基本实现
    // 创建Observer 或者 Subscriber  大人, 观察者
    // 创建Observable     熊孩子, 被观察者
    private void testRxjava() {

        /**
         * onNext : 普通事件, 根据参数响应
         * onCompleted : 特殊事件, 事件队列完结, 当全都干完了, 不会有新的next 发出执行
         * onError : 特殊事件 , 出错. 之后不会调用 onCompleted
         */


        //创建 Observer
        //Observer 即观察者，它决定事件触发的时候将有怎样的行为。 RxJava 中的 Observer 接口的实现方式：
        Observer<String> observer = new Observer<String>() {
            @Override
            public void onNext(String s) {
                Log.d(tag, "Item: " + s);
            }

            @Override
            public void onCompleted() {
                Log.d(tag, "Completed!");
            }

            @Override
            public void onError(Throwable e) {
                Log.d(tag, "Error!");
            }

        };


        //除了 Observer 接口之外，RxJava 还内置了一个实现了 Observer 的抽象类：Subscriber。
        // Subscriber 对 Observer 接口进行了一些扩展，但他们的基本使用方式是完全一样的：
        Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onNext(String s) {
                Log.d(tag, "Item: " + s);
            }

            @Override
            public void onCompleted() {
                Log.d(tag, "Completed!");
            }

            @Override
            public void onError(Throwable e) {
                Log.d(tag, "Error!");
            }

            /**
             * 这是 Subscriber 增加的方法。它会在 subscribe 刚开始，而事件还未发送之前被调用，
             * 可以用于做一些准备工作，例如数据的清零或重置。这是一个可选方法，默认情况下它的实现为空。
             * 需要注意的是，如果对准备工作的线程有要求（例如弹出一个显示进度的对话框，这必须在主线程执行），
             * onStart() 就不适用了，因为它总是在 subscribe 所发生的线程被调用，而不能指定线程。
             * 要在指定的线程来做准备工作，可以使用 doOnSubscribe() 方法，具体可以在后面的文中看到。
             */
            @Override
            public void onStart() {
                super.onStart();
            }
        };

        /**
         * unsubscribe(): 这是 Subscriber 所实现的另一个接口 Subscription 的方法，
         * 用于取消订阅。在这个方法被调用后，Subscriber 将不再接收事件。
         * 一般在这个方法调用前，可以使用 isUnsubscribed() 先判断一下状态。
         * unsubscribe() 这个方法很重要，因为在 subscribe() 之后，
         * Observable 会持有 Subscriber 的引用，这个引用如果不能及时被释放，
         * 将有内存泄露的风险。所以最好保持一个原则：
         * 要在不再使用的时候尽快在合适的地方（例如 onPause() onStop() 等方法中）
         * 调用 unsubscribe() 来解除引用关系，以避免内存泄露的发生。
         */
        //     subscriber.unsubscribe();
        //   subscriber.isUnsubscribed();


        /**
         * 创建 Observable
         Observable 即被观察者，它决定什么时候触发事件以及触发怎样的事件。
         RxJava 使用 create() 方法来创建一个 Observable ，并为它定义事件触发规则：
         */
        Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("Hello");
                subscriber.onNext("Hi");
                subscriber.onNext("Aloha");
                subscriber.onCompleted();
            }
        });

        // 两种简化写法 这个狗篮子 被观察者就是个猴孩子, 闹事儿的.
        // 把这几个作为参数, 依次传到 onnext() 方法
        //  Observable observable = Observable.just("Hello", "Hi", "Aloha");

        //  String[] words = {"Hello", "Hi", "Aloha"};
        //  Observable observable = Observable.from(words);


        /**
         * 可以看到，这里传入了一个 OnSubscribe 对象作为参数。
         * OnSubscribe 会被存储在返回的 Observable 对象中，它的作用相当于一个计划表，
         * 当 Observable 被订阅的时候，OnSubscribe 的 call() 方法会自动被调用，
         * 事件序列就会依照设定依次触发（对于上面的代码，就是观察者Subscriber
         * 将会被调用三次 onNext() 和一次 onCompleted()）。
         * 这样，由被观察者调用了观察者的回调方法，
         * 就实现了由被观察者向观察者的事件传递，即观察者模式。

         这个例子很简单：事件的内容是字符串，而不是一些复杂的对象；
         事件的内容是已经定好了的，而不像有的观察者模式一样是待确定的
         （例如网络请求的结果在请求返回之前是未知的）；
         所有事件在一瞬间被全部发送出去，
         而不是夹杂一些确定或不确定的时间间隔或者经过某种触发器来触发的。
         总之，这个例子看起来毫无实用价值。但这是为了便于说明，
         实质上只要你想，各种各样的事件发送规则你都可以自己来写。
         至于具体怎么做，后面都会讲到，但现在不行。
         只有把基础原理先说明白了，上层的运用才能更容易说清楚。
         */

        /**
         *  Subscribe (订阅)
         *  创建了 Observable 和 Observer 之后，再用 subscribe() 方法将它们联结起来，
         *  整条链子就可以工作了。代码形式很简单：
         */


        // observable.subscribe(observer);
        // 或者：被观察者, 订阅, 观察者
        observable.subscribe(subscriber);


    }

    //  不完整回调
    private void buwanzhenghuidiao() {


        //熊孩子, 吃饭喝水撒尿
        Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("Hello");
                subscriber.onNext("Hi");
                subscriber.onNext("Aloha");
                subscriber.onCompleted();
            }
        });


        // 除了 subscribe(Observer) 和 subscribe(Subscriber) ，subscribe()
        // 还支持不完整定义的回调，RxJava 会自动根据定义创建出 Subscriber 。形式如下：

        Action1<String> onNextAction = new Action1<String>() {
            // onNext()
            @Override
            public void call(String s) {
                Log.d(tag, s);
            }
        };
        Action1<Throwable> onErrorAction = new Action1<Throwable>() {
            // onError()
            @Override
            public void call(Throwable throwable) {
                // Error handling
            }
        };
        Action0 onCompletedAction = new Action0() {
            // onCompleted()
            @Override
            public void call() {
                Log.d(tag, "completed");
            }
        };

        // 自动创建 Subscriber ，并使用 onNextAction 来定义 onNext()
        observable.subscribe(onNextAction);// hello hi Aloha
        // 自动创建 Subscriber ，并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
        observable.subscribe(onNextAction, onErrorAction);// hello hi Aloha
        // 自动创建 Subscriber ，并使用 onNextAction、 onErrorAction 和
        // onCompletedAction 来定义 onNext()、 onError() 和 onCompleted()
        observable.subscribe(onNextAction, onErrorAction, onCompletedAction);// hello hi Aloha completed
    }

    //测试出了异常都调用啥方法.
    private void ceshicuowuqingkuangxiafangfadiaoyong() {


        //熊孩子, 吃饭喝水撒尿
        Observable observable = Observable.create(new Observable.OnSubscribe<Object>() {
            @Override
            public void call(Subscriber<? super Object> subscriber) {
                subscriber.onNext("Hello");
                subscriber.onNext(2);
                subscriber.onNext("Aloha");
                subscriber.onCompleted();
            }
        });


        // 除了 subscribe(Observer) 和 subscribe(Subscriber) ，subscribe()
        // 还支持不完整定义的回调，RxJava 会自动根据定义创建出 Subscriber 。形式如下：

        Action1<Object> onNextAction = new Action1<Object>() {
            // onNext()
            @Override
            public void call(Object s) {
                //打印字符串,第二个传的是 2 强转会报错.
                String a = (String) s;
                Log.d(tag, a);
            }
        };
        Action1<Throwable> onErrorAction = new Action1<Throwable>() {
            // onError()
            @Override
            public void call(Throwable throwable) {
                // Error handling
                Log.d(tag, "错误");
                Log.d(tag, throwable.toString());
                throwable.printStackTrace();//还是这个好, 这个直接显示行号.
                //       throwable.printStackTrace();
            }
        };
        Action0 onCompletedAction = new Action0() {
            // onCompleted()
            @Override
            public void call() {
                Log.d(tag, "completed");
            }
        };


        // 自动创建 Subscriber ，并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
        observable.subscribe(onNextAction, onErrorAction);// hello hi Aloha

        /**
         *  测试结果,良好, 出错之后, 队列不再执行. 同时为了排错容易,在log得时候应该标识出
         具体位置

         03-22 16:26:15.261 2160-2160/com.vxb.testmvp D/tag: Hello
         03-22 16:26:15.262 2160-2160/com.vxb.testmvp D/tag: 错误
         03-22 16:26:15.262 2160-2160/com.vxb.testmvp D/tag: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

         */


    }


    //场景示例 One
    private void changjingjuli1() {
        //   打印字符串数组
        //   将字符串数组 names 中的所有字符串依次打印出来：

        String[] names = new String[]{"sunxiaoxing", "moumoumou"};
        Observable.from(names)
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String name) {
                        Log.d(tag, name);
                    }
                });
    }


    //场景示例 Two
    private void changjingjuli2() {
        // 由 id 取得图片并显示
        // 由指定的一个 drawable 文件 id drawableRes 取得图片，
        // 并显示在 ImageView 中，并在出现异常的时候打印 Toast 报错：

        final int drawableRes = R.drawable.cheese_1;
        Observable.create(new Observable.OnSubscribe<Drawable>() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void call(Subscriber<? super Drawable> subscriber) {
                Drawable drawable = getTheme().getDrawable(drawableRes);
                subscriber.onNext(drawable);
                subscriber.onCompleted();
            }
        }).subscribe(new Observer<Drawable>() {
            @Override
            public void onNext(Drawable drawable) {
                imageView.setImageDrawable(drawable);
            }

            @Override
            public void onCompleted() {
            }

            @Override
            public void onError(Throwable e) {
                Toast.makeText(TestRxJaveActivity.this, "Error!", Toast.LENGTH_SHORT).show();
            }
        });
    }


    //线程操作测试
    private void xianchengcaozuoceshi1() {
        /**
         * 线程控制 —— Scheduler (一)

         在不指定线程的情况下， RxJava 遵循的是线程不变的原则，即：在哪个线程调用 subscribe()，就在哪个线程生产事件；在哪个线程生产事件，就在哪个线程消费事件。如果需要切换线程，就需要用到 Scheduler （调度器）。

         1) Scheduler 的 API (一)

         在RxJava 中，Scheduler ——调度器，相当于线程控制器，RxJava 通过它来指定每一段代码应该运行在什么样的线程。RxJava 已经内置了几个 Scheduler ，它们已经适合大多数的使用场景：

         Schedulers.immediate(): 直接在当前线程运行，相当于不指定线程。这是默认的 Scheduler。
         Schedulers.newThread(): 总是启用新线程，并在新线程执行操作。
         Schedulers.io(): I/O 操作（读写文件、读写数据库、网络信息交互等）所使用的 Scheduler。行为模式和 newThread() 差不多，区别在于 io() 的内部实现是是用一个无数量上限的线程池，可以重用空闲的线程，因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中，可以避免创建不必要的线程。
         Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算，即不会被 I/O 等操作限制性能的操作，例如图形的计算。这个 Scheduler 使用的固定的线程池，大小为 CPU 核数。不要把 I/O 操作放在 computation() 中，否则 I/O 操作的等待时间会浪费 CPU。
         另外， Android 还有一个专用的 AndroidSchedulers.mainThread()，它指定的操作将在 Android 主线程运行。
         有了这几个 Scheduler ，就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。 * subscribeOn(): 指定 subscribe() 所发生的线程，即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。 * observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。

         文字叙述总归难理解，上代码： */

        Observable.just(1, 2, 3, 4)
                .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer number) {
                        Log.d(tag, "number:" + number);
                    }
                });

        /*
        上面这段代码中，由于 subscribeOn(Schedulers.io()) 的指定，
        被创建的事件的内容 1、2、3、4 将会在 IO 线程发出；
        而由于 observeOn(AndroidScheculers.mainThread()) 的指定，
        因此 subscriber 数字的打印将发生在主线程 。事实上，
        这种在 subscribe() 之前写上两句 subscribeOn(Scheduler.io())
        和 observeOn(AndroidSchedulers.mainThread()) 的使用方式非常常见，
        它适用于多数的 『后台线程取数据，主线程显示』的程序策略。
         */

    }

    //而前面提到的由图片 id 取得图片并显示的例子，如果也加上这两句：
    private void xianchengcaozuoceshi2() {
        final int drawableRes = R.drawable.cheese_1;
        Observable.create(new Observable.OnSubscribe<Drawable>() {

            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void call(Subscriber<? super Drawable> subscriber) {
                Drawable drawable = getTheme().getDrawable(drawableRes);
                subscriber.onNext(drawable);
                subscriber.onCompleted();
            }
        })
                .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
                .subscribe(new Observer<Drawable>() {
                    @Override
                    public void onNext(Drawable drawable) {
                        imageView.setImageDrawable(drawable);
                    }

                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        Toast.makeText(TestRxJaveActivity.this, "Error!", Toast.LENGTH_SHORT).show();
                    }
                });

        /*
        那么，加载图片将会发生在 IO 线程，而设置图片则被设定在了主线程。
        这就意味着，即使加载图片耗费了几十甚至几百毫秒的时间，也不会造成丝毫界面的卡顿。
         */
    }

    // 变换  操作符 map()
    private void bianhuanTestMap() {

        Observable.just("images/logo.png") // 输入类型 String
                .map(new Func1<String, Bitmap>() {
                    @Override
                    public Bitmap call(String filePath) { // 参数类型 String
                        return getBitmapFromPath(filePath); // 返回类型 Bitmap
                    }
                })
                .subscribe(new Action1<Bitmap>() {
                    @Override
                    public void call(Bitmap bitmap) { // 参数类型 Bitmap
                        showBitmap(bitmap);
                    }

                });

    }

    // 变换   介绍flatmap 出现的场景    操作符 flatMap()  实例1
    private void bianhuanTestflatMap1() {
        /*
        flatMap(): 这是一个很有用但非常难理解的变换，因此我决定花多些篇幅来介绍它。
        首先假设这么一种需求：假设有一个数据结构『学生』，现在需要打印出一组学生的名字。实现方式很简单：*/
        Student[] students = getStudents();
        Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(String name) {
                Log.d(tag, name);
            }
        };

        Observable.from(students)
                .map(new Func1<Student, String>() {
                    @Override
                    public String call(Student student) {
                        return student.getName();
                    }
                })
                .subscribe(subscriber);


    }

    // 变换 介绍flatmap 出现的场景   操作符 flatMap() 实例2
    private void bianhuanTestflatMap2() {
        /*
        很简单。那么再假设：如果要打印出每个学生所需要修的所有课程的名称呢？
        （需求的区别在于，每个学生只有一个名字，但却有多个课程。）首先可以这样实现：*/


        Student[] students = getStudents();
        Subscriber<Student> subscriber = new Subscriber<Student>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }


            @Override
            public void onNext(Student student) {
                List<Course> courses = student.getCourses();
                for (int i = 0; i < courses.size(); i++) {
                    Course course = courses.get(i);
                    Log.d(tag, course.getName());
                }
            }
        };

        Observable.from(students).subscribe(subscriber);


        /**
         * 依然很简单。那么如果我不想在 Subscriber 中使用 for 循环，而是希望 Subscriber
         * 中直接传入单个的 Course 对象呢（这对于代码复用很重要）？用 map() 显然是不行的，
         * 因为 map() 是一对一的转化，而我现在的要求是一对多的转化。
         * 那怎么才能把一个 Student 转化成多个 Course 呢？
         这个时候，就需要用 flatMap() 了：
         */

    }


    // 变换 介绍flatmap 出现的场景   操作符 flatMap() 实例3
    private void bianhuanTestflatMap3() {

        Student[] students = getStudents();

        Subscriber<Course> subscriber = new Subscriber<Course>() {

            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Course course) {
                Log.d(tag, course.getName());
            }
        };


        Observable.from(students)
                .flatMap(new Func1<Student, Observable<Course>>() {// 尖括号 里面, 输入参数类型, 返回值类型.
                    @Override
                    public Observable<Course> call(final Student student) {

                        Observable<Course> sss = Observable.from(student.getCourses());
                 /*       Observable.create(new Observable.OnSubscribe<Course>() {
                            @Override
                            public void call(Subscriber<? super Course> subscriber) {
                                subscriber.onNext(student.getCourses().get(0));
                                subscriber.onNext(student.getCourses().get(1));
                                subscriber.onNext(student.getCourses().get(2));
                                subscriber.onNext(student.getCourses().get(3));
                            }
                        });*/
                        return sss;
                    }
                }).subscribe(subscriber);

    }


    private void showBitmap(Bitmap bitmap) {
    }

    private Bitmap getBitmapFromPath(String filePath) {
        return null;
    }

    private Student[] getStudents() {

        List<Course> listSun = new ArrayList<Course>();
        listSun.add(new Course("语文", "语文老师"));
        listSun.add(new Course("数学", "语文老师"));
        listSun.add(new Course("英语", "语文老师"));

        List<Course> listSu = new ArrayList<Course>();
        listSun.add(new Course("数学", "语文老师"));
        listSun.add(new Course("英语", "语文老师"));

        Student sun = new Student("孙小星", 23, listSun);
        Student su = new Student("苏露娜", 19, listSu);

        Student[] students = {sun, su};

        return students;
    }


}
